﻿namespace KanbanBoard.Server.Command.DomainModel {
    internal class Priority {
        private Priority higher;
        private int numericPriority;
        private Priority lower;

        private Priority() {}

        internal void PutAbove(Priority priority) {
            InsertBetween(priority.higher, priority);
        }

        internal void PutBelow(Priority priority) {
            InsertBetween(priority, priority.lower);
        }

        internal void StartNewPriorityChain() {
            Exclude();

            higher = new HighestPriority(this);
            numericPriority = 1;
            lower = new LowestPriority(this);
        }

        private void Exclude() {
            higher.lower = lower;
            lower.higher = higher;
            lower.UpdateNumericPriority();
        }

        private void InsertBetween(Priority higher, Priority lower) {
            if (this == higher || this == lower) return;

            Exclude();

            this.higher = higher;
            this.lower = lower;
            lower.higher = this;
            higher.lower = this;

            UpdateNumericPriority();
        }

        protected internal virtual void UpdateNumericPriority() {
            numericPriority = higher.numericPriority + 1;
            lower.UpdateNumericPriority();
        }

        internal static Priority Undefined {
            get {
                var priority = new Priority();
                priority.higher = new HighestPriority(priority);
                priority.numericPriority = 1;
                priority.lower = new LowestPriority(priority);
                return priority;
            }
        }

        #region Special Case Priorities

        private class HighestPriority : Priority {
            private HighestPriority() {}

            internal HighestPriority(Priority lower) {
                numericPriority = 0;
                this.lower = lower;
            }
        }

        private class LowestPriority : Priority {
            private LowestPriority() {}

            internal LowestPriority(Priority higher) {
                this.higher = higher;
            }

            protected internal override void UpdateNumericPriority() {}
        }

        #endregion
    }
}